package com.wondersgroup.core.util;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.oned.Code128Reader;
import com.google.zxing.oned.EAN13Reader;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

/**
 * 处理二维码和条形码的编码和解码
 *
 * @author wanglei
 */
public class ZxingUtil {

    /**
     * 二维码编码
     *
     * @param contents
     * @param width
     * @param height
     * @param file
     * @throws com.google.zxing.WriterException
     * @throws java.io.IOException
     */
    public static void encodeQrCode(String contents, int width, int height, File file) throws WriterException, IOException {
        Map<EncodeHintType, Object> hints = new HashMap<>();
        // 指定纠错等级  
        //L水平   7%的字码可被修正  
        //M水平   15%的字码可被修正  
        //Q水平   25%的字码可被修正  
        //H水平   30%的字码可被修正  
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
        // 指定编码格式  
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(EncodeHintType.MARGIN, 0);

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            BitMatrix bitMatrix = new MultiFormatWriter().encode(contents,
                    BarcodeFormat.QR_CODE, width, height, hints);

            MatrixToImageConfig imageConfig = new MatrixToImageConfig(MatrixToImageConfig.BLACK, MatrixToImageConfig.WHITE);

            MatrixToImageWriter.writeToStream(bitMatrix, "png", fos, imageConfig);
        } catch(Exception e){
        	e.printStackTrace();
        }finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException ex) {
            }
        }
    }

    /**
     * EAN13条形码编码(内容定长13, 图片最小长度113)
     *
     * @param contents
     * @param width
     * @param height
     * @param file
     * @throws com.google.zxing.WriterException
     * @throws java.io.IOException
     */
    public static void encodeEAN13(String contents, int width, int height, File file) throws WriterException, IOException {
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.MARGIN, 0);

        int codeWidth = 11 + //左侧空白区
                3 + // 起始符
                (7 * 6) + // 左侧数据符
                5 + // 中间分隔符
                (7 * 5) + // 右侧数据符
                7 + // 校验符
                3 + // 终止符
                7; // 右侧空白区
        codeWidth = Math.max(codeWidth, width);
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            BitMatrix bitMatrix = new MultiFormatWriter().encode(contents,
                    BarcodeFormat.EAN_13, codeWidth, height, null);

            MatrixToImageWriter.writeToStream(bitMatrix, "png", fos);
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException ex) {
            }
        }
    }

    /**
     * Code128条形码编码(最大内容长度128, 可变长)
     *
     * @param contents
     * @param width
     * @param height
     * @param file
     * @throws com.google.zxing.WriterException
     * @throws java.io.IOException
     */
    public static void encodeCode128(String contents, int width, int height, File file) throws WriterException, IOException {
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.MARGIN, 0);

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            BitMatrix bitMatrix = new MultiFormatWriter().encode(contents,
                    BarcodeFormat.CODE_128, width, height, null);

            MatrixToImageWriter.writeToStream(bitMatrix, "png", fos);
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException ex) {
            }
        }
    }

    /**
     * 解码, 自动识别格式
     *
     * @param file
     * @return
     * @throws NotFoundException
     * @throws IOException
     */
    public static String decode(File file) throws NotFoundException, IOException {
        BufferedImage image = ImageIO.read(file);
        if (image == null) {
            Logger.getLogger(ZxingUtil.class.getName()).log(Level.WARNING, "the decode image may be not exit.");
        }
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

        Map<DecodeHintType, Object> hints = new HashMap<>();
        hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");

        Set<BarcodeFormat> barcodeFormats = new HashSet<>();
        barcodeFormats.add(BarcodeFormat.QR_CODE);
        barcodeFormats.add(BarcodeFormat.DATA_MATRIX);
        barcodeFormats.add(BarcodeFormat.AZTEC);
        barcodeFormats.add(BarcodeFormat.PDF_417);
        barcodeFormats.add(BarcodeFormat.MAXICODE);
        barcodeFormats.add(BarcodeFormat.EAN_8);
        barcodeFormats.add(BarcodeFormat.EAN_13);
        barcodeFormats.add(BarcodeFormat.CODABAR);
        barcodeFormats.add(BarcodeFormat.CODE_39);
        barcodeFormats.add(BarcodeFormat.CODE_93);
        barcodeFormats.add(BarcodeFormat.CODE_128);
        barcodeFormats.add(BarcodeFormat.ITF);
        barcodeFormats.add(BarcodeFormat.RSS_14);
        barcodeFormats.add(BarcodeFormat.RSS_EXPANDED);
        barcodeFormats.add(BarcodeFormat.UPC_A);
        barcodeFormats.add(BarcodeFormat.UPC_E);
        hints.put(DecodeHintType.POSSIBLE_FORMATS, barcodeFormats);

        Result result = new MultiFormatReader().decode(bitmap, hints);
        return result.getText();
    }

    /**
     * 二维码解码
     *
     * @param file
     * @return String
     * @throws java.io.IOException
     * @throws com.google.zxing.NotFoundException
     * @throws com.google.zxing.ChecksumException
     * @throws com.google.zxing.FormatException
     */
    public static String decodeQrCode(File file) throws IOException, NotFoundException, ChecksumException, FormatException {
        BufferedImage image = ImageIO.read(file);
        if (image == null) {
            Logger.getLogger(ZxingUtil.class.getName()).log(Level.WARNING, "the decode image may be not exit.");
        }
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

        Map<DecodeHintType, Object> hints = new HashMap<>();
        hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");

        Result result = new QRCodeReader().decode(bitmap, hints);
        return result.getText();
    }

    /**
     * EAN13条形码解码
     *
     * @param file
     * @return String
     * @throws java.io.IOException
     * @throws com.google.zxing.NotFoundException
     * @throws com.google.zxing.FormatException
     */
    public static String decodeEAN13(File file) throws IOException, NotFoundException, FormatException {
        BufferedImage image = ImageIO.read(file);
        if (image == null) {
            Logger.getLogger(ZxingUtil.class.getName()).log(Level.WARNING, "the decode image may be not exit.");
        }
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

        Result result = new EAN13Reader().decode(bitmap, null);
        return result.getText();
    }

    /**
     * Code128条形码解码
     *
     * @param file
     * @return String
     * @throws java.io.IOException
     * @throws com.google.zxing.NotFoundException
     * @throws com.google.zxing.FormatException
     */
    public static String decodeCode128(File file) throws IOException, NotFoundException, FormatException {
        BufferedImage image = ImageIO.read(file);
        if (image == null) {
            Logger.getLogger(ZxingUtil.class.getName()).log(Level.WARNING, "the decode image may be not exit.");
        }
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

        Result result = new Code128Reader().decode(bitmap, null);
        return result.getText();
    }
}
